(function (angular) {
    'use strict';
    angular.module("fs.validator")
        .factory("validateUtil", validateUtil)
        .factory("required", requiredService)
        .factory("atleastonerequired", atLeastOneRequiredService)
        .factory("dependent", dependentService)
        .factory("lengthcheck", lengthCheckService)
        .factory("minlength", minLengthService)
        .factory("maxlength", maxLengthService)
        .factory("rangevalue", rangeValueService)
        .factory("minvalue", minValueService)
        .factory("maxvalue", maxValueService)
        .factory("bigdecimalrange", bigDecimalRangeService)
        .factory("mimes", mimesService)
        .factory("lookupcheck", lookupCheckService)
        .factory("patterncheck", patternCheckService)
        .factory("holidaycheck", holidayCheckService)
        .factory("emailcheck", emailCheckService)
        .factory("urlcheck", urlCheckService)
        .factory("dateformatcheck", dateFormatCheckService)
        .factory("uniquecheck", uniqueCheckService)
        .factory("equalsto", equalsToService)
        .factory("datecompare", dateCompareService)
        .factory("datebetweencompare", dateBetweenService)
        .factory("numbercompare", numberCompareService)
        .factory("rowcompare", rowCompareService)
        .factory("rowuniquecheck", rowUniqueCheckService)
        .factory("uniqueCheckService", uniqueCheckServiceFactory)
        .factory("lookupCheckService", lookupCheckServiceFactory);

    requiredService.$inject = ['$q', '$translate', '$filter'];
    function requiredService($q, $translate, $filter) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, fromValue, toValue;
                if (modelValue && rule.specialModelTypeFlag) {
                    fromValue = modelValue.from;
                    toValue = modelValue.to;
                } else {
                    value = viewValue || (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue);
                }
                if (!rule.specialModelTypeFlag && isEmpty(value)) {
                    buildErrorMsg(rule, deferred, $translate);
                } else if (rule.specialModelTypeFlag && (isEmpty(fromValue) || isEmpty(toValue))) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    atLeastOneRequiredService.$inject = ['$q', '$translate', '$parse'];
    function atLeastOneRequiredService($q, $translate, $parse) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), checkedFields = rule.checkedFields;
                for (var i = 0; i < checkedFields.length; i++) {
                    var subArray = checkedFields[i], subGroupValidFlag = false;
                    for (var j = 0; j < subArray.length; j++) {
                        var field = subArray[j], targetValue, fromValue, toValue;
                        modelValue = rule.ngModel ? $parse(field)(rule.ngModel) : null;
                        if (modelValue && isSpecialModelType(modelValue)) {
                            fromValue = modelValue.from;
                            toValue = modelValue.to;
                        } else {
                            targetValue = rule.ngModel ? $parse(field)(rule.ngModel) : null;
                        }
                        if (!isSpecialModelType(modelValue) && !isEmpty(targetValue)) {
                            subGroupValidFlag = true;
                            break;
                        } else if (isSpecialModelType(modelValue) && (!isEmpty(fromValue) && !isEmpty(toValue))) {
                            subGroupValidFlag = true;
                            break;
                        }
                    }
                    if (subGroupValidFlag) break;
                }
                if (subGroupValidFlag) {
                    deferred.resolve();
                } else {
                    buildErrorMsg(rule, deferred, $translate);
                }
                return deferred.promise;
            }
        };
    }

    dependentService.$inject = ['$q', '$translate', '$parse', '$filter'];
    function dependentService($q, $translate, $parse, $filter) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), conditionFieldValue, conditionValue, value, conditionPattern = rule.conditionPattern, isValid = true, conditionRequiredFlag = rule.conditionRequiredFlag, conditionFieldValueIndex = -1;
                value = (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue) || viewValue;
                conditionFieldValue = $parse(rule.conditionField)(rule.ngModel);
                if (!conditionFieldValue && conditionFieldValue !== false && conditionFieldValue != 0)
                    conditionFieldValue = $parse("vm." + rule.conditionField)(rule.appScope);
                conditionValue = (angular.isArray(rule.conditionValue)) ? rule.conditionValue : [];
                var getConditionFieldValueIndex = function (arrays, objStr) {
                    for (var i = 0; i < arrays.length; i++) {
                        if (arrays[i] === objStr) {
                            return i;
                            break;
                        }
                    }
                    return -1;
                };
                conditionFieldValueIndex = getConditionFieldValueIndex(conditionValue, conditionFieldValue);
                if (!isEmpty(conditionPattern) && conditionFieldValueIndex != -1) {
                    rule.conditionFieldValueIndex = conditionFieldValueIndex;
                    conditionPattern = new RegExp(conditionPattern.split(";")[conditionFieldValueIndex]);
                    if (conditionRequiredFlag && isEmpty(value)) {
                        isValid = false;
                        rule.validateRequiredFlag = true;
                    } else if (!isEmpty(value) && !isEmpty(conditionPattern)) {
                        rule.validateRequiredFlag = false;
                        isValid = conditionPattern.test(value);
                    }
                } else {
                    isValid = !isEmpty(value);
                }
                if (!isEmpty(conditionValue) && conditionFieldValueIndex != -1 && !isValid) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    lengthCheckService.$inject = ['$q', '$translate', '$filter'];
    function lengthCheckService($q, $translate, $filter) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, min, max;
                value = (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue) || viewValue;
                min = (rule.min) ? Number(rule.min) : null;
                max = (rule.max) ? Number(rule.max) : null;
                if (!isEmpty(value) && !isEmpty(min) && !isEmpty(max) && (value.length < min || value.length > max)) {
                    buildErrorMsg(rule, deferred, $translate);
                } else if (!isEmpty(value) && !isEmpty(min) && isEmpty(max) && value.length < min) {
                    //for minLenth
                    buildErrorMsg(rule, deferred, $translate);
                } else if (!isEmpty(value) && !isEmpty(max) && isEmpty(min) && value.length > max) {
                    //for maxLenth
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    minLengthService.$inject = ['$translate'];
    function minLengthService($injector) {
        return $injector.get('lengthcheck');
    }

    maxLengthService.$inject = ['$injector'];
    function maxLengthService($injector) {
        return $injector.get('lengthcheck');
    }

    rangeValueService.$inject = ['$q', '$translate', '$filter'];
    function rangeValueService($q, $translate, $filter) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, min, max, allowEqual;
                value = (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue) || viewValue;
                min = (rule.min) ? Number(rule.min) : null;
                max = (rule.max) ? Number(rule.max) : null;
                allowEqual = (rule.allowEqual != undefined) ? rule.allowEqual : true;
                if (!isEmpty(value) && angular.isNumber(Number(value))) {
                    if (allowEqual) {
                        if (!isEmpty(value) && !isEmpty(min) && !isEmpty(max) && (value < min || value > max)) {
                            buildErrorMsg(rule, deferred, $translate);
                        } else if (!isEmpty(value) && !isEmpty(min) && isEmpty(max) && value < min) {
                            //for minLenth
                            buildErrorMsg(rule, deferred, $translate);
                        } else if (!isEmpty(value) && !isEmpty(max) && isEmpty(min) && value > max) {
                            //for maxLenth
                            buildErrorMsg(rule, deferred, $translate);
                        } else {
                            deferred.resolve();
                        }
                    } else {
                        if (!isEmpty(value) && !isEmpty(min) && !isEmpty(max) && (value <= min || value >= max)) {
                            buildErrorMsg(rule, deferred, $translate);
                        } else if (!isEmpty(value) && !isEmpty(min) && isEmpty(max) && value <= min) {
                            //for minLenth
                            buildErrorMsg(rule, deferred, $translate);
                        } else if (!isEmpty(value) && !isEmpty(max) && isEmpty(min) && value >= max) {
                            //for maxLenth
                            buildErrorMsg(rule, deferred, $translate);
                        } else {
                            deferred.resolve();
                        }
                    }
                } else if (isEmpty(value)) {
                    deferred.resolve();
                } else {
                    buildErrorMsg(rule, deferred, $translate);
                }
                return deferred.promise;
            }
        };
    }

    minValueService.$inject = ['$injector'];
    function minValueService($injector) {
        return $injector.get('rangevalue');
    }

    maxValueService.$inject = ['$injector'];
    function maxValueService($injector) {
        return $injector.get('rangevalue');
    }

    bigDecimalRangeService.$inject = ['$q', '$translate', '$filter', 'FormatterConstants', 'storageService'];
    function bigDecimalRangeService($q, $translate, $filter, FormatterConstants, storageService) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, scale, maxPrecision, minPrecision, fromValue, toValue;
                //set the fractionDigits begin
                var scope = rule.$scope;
                var getPattern = function (fsFormat) {
                    var _pattern;
                    if (fsFormat && fsFormat.match('[#0]')) {
                        _pattern = fsFormat;
                    } else {
                        // for moneyFormat the pattern may be 'USD' or 'vm.product.loanAmtRangeCcy' or 'loanAmtRangeCcy' or 'moeny0' etc
                        if (!fsFormat) {
                            return null;
                        }
                        _pattern = $parse(fsFormat)(scope.fsGridConfig ? scope.fsGridConfig.appScope : scope);
                        if (isEmpty(_pattern))
                            _pattern = $parse(fsFormat)(scope.ngModel);
                        //for sensitive
                        if (isEmpty(_pattern))
                            _pattern = $parse(fsFormat)(scope);
                    }
                    return _pattern || fsFormat;

                };
                var pattern = getPattern(rule.fsFormat);
                var formatInfo, siteInfo, ccyConfig = FormatterConstants.CCY_CONFIG, fractionDigits;
                formatInfo = storageService.getFormatInfo() || {};
                siteInfo = storageService.getSiteInfo() || {};
                if (ccyConfig[angular.uppercase(pattern)] == undefined) {
                    fractionDigits = ccyConfig[siteInfo.baseCcy];
                } else {
                    fractionDigits = ccyConfig[angular.uppercase(pattern)];
                }
                rule.fractionDigits = fractionDigits;
                if (rule.fractionDigits == undefined) {
                    rule.fractionDigits = rule.scale;
                } else {
                    rule.scale = rule.fractionDigits;
                }
                //set the fractionDigits begin end

                scale = (rule.scale) ? Number(rule.scale) : null;
                maxPrecision = (rule.maxPrecision) ? Number(rule.maxPrecision) : null;
                minPrecision = (rule.minPrecision) ? Number(rule.minPrecision) : 0;
                if (modelValue && isSpecialModelType(modelValue)) {
                    fromValue = rule.needFilterModelValue ? $filter('encode')(modelValue.from) : modelValue.from;
                    toValue = rule.needFilterModelValue ? $filter('encode')(modelValue.to) : modelValue.to;
                } else {
                    value = (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue) || viewValue;
                }
                var getValidResult = function (value, scale, maxPrecision, minPrecision) {
                    if (isEmpty(value) || isEmpty(maxPrecision) || isEmpty(minPrecision)) {
                        return true;
                    }
                    value = value.toString();
                    var integerBitLength = 0;
                    var actualScale = 0;
                    if (value.indexOf(".") != -1) {
                        integerBitLength = value.indexOf(".");
                        actualScale = value.substring(value.indexOf(".") + 1, value.length).length;
                    } else {
                        integerBitLength = value.length;
                        actualScale = 0;
                    }
                    var isValid = (integerBitLength >= minPrecision - scale && integerBitLength <= maxPrecision - scale && actualScale <= scale);
                    return isValid;
                }
                if (!isSpecialModelType(modelValue) && !isEmpty(value)) {
                    var isValid = getValidResult(value, scale, maxPrecision, minPrecision);
                    if (!isValid) {
                        buildErrorMsg(rule, deferred, $translate);
                    } else {
                        deferred.resolve();
                    }
                } else if (isSpecialModelType(modelValue) && (!isEmpty(fromValue) || !isEmpty(toValue))) {
                    var isFromValid = getValidResult(fromValue, scale, maxPrecision, minPrecision);
                    var isToValid = getValidResult(toValue, scale, maxPrecision, minPrecision);
                    if (!isFromValid || !isToValid) {
                        buildErrorMsg(rule, deferred, $translate);
                    } else {
                        deferred.resolve();
                    }
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    mimesService.$inject = ['$q', '$translate', '$filter'];
    function mimesService($q, $translate, $filter) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, mimes, mimesArray, extentsion, flag;
                value = viewValue || (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue) || '';
                mimes = rule.mimes;
                mimesArray = angular.isArray(mimes) ? mimes : mimes.split(",");
                extentsion = value.substring(value.lastIndexOf(".") + 1, value.length).toLowerCase();
                flag = false;
                angular.forEach(mimesArray, function (data, index, array) {
                    if (!flag && extentsion == data) {
                        flag = true;
                    }
                });
                if (!(isEmpty(value) || flag)) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    lookupCheckService.$inject = ['$q', '$translate', '$parse', 'lookupCheckService', '$timeout', '$filter'];
    function lookupCheckService($q, $translate, $parse, lookupCheckService, $timeout, $filter) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, params = {}, hasNullValue = false, ruleParams = {};
                value = (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue) || viewValue;
                if (!isEmpty(value)) {
                    ruleParams = rule.params;
                    params['service'] = rule.service;
                    angular.forEach(ruleParams, function (value, key) {
                        if (value && value.charAt(0) == '<') {
                            params[key] = rule.ngModel ? $parse(value.substr(1))(rule.ngModel) : null;
                        } else {
                            params[key] = value;
                        }
                        if (isEmpty(params[key])) hasNullValue = true;
                    });
                    if (!hasNullValue) {
                        lookupCheckService.lookupCheck(params).then(function (response) {
                            var isValid = response;
                            if (!isValid) {
                                rule.operatedFieldValue = value;
                                buildErrorMsg(rule, deferred, $translate);
                            } else {
                                deferred.resolve();
                            }
                        }, function (error) {
                            deferred.reject(error);
                        });
                    } else {
                        deferred.resolve({});
                    }

                } else {
                    deferred.resolve({});
                }
                return deferred.promise;
            }
        };
    }
    patternCheckService.$inject = ['$q', '$translate', '$filter', 'PATTERN_TYPES'];
    function patternCheckService($q, $translate, $filter, PATTERN_TYPES) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, pattern, fromValue, toValue, dateFormat = rule.dateInputFormat || "date";
                if (modelValue && isSpecialModelType(modelValue)) {
                    if (angular.isDate(modelValue.from)) {
                        fromValue = formatService.formatDate(modelValue.from, dateFormat);
                    } else {
                        fromValue = modelValue.from;
                    }
                    if (angular.isDate(modelValue.to)) {
                        toValue = formatService.formatDate(modelValue.to, dateFormat);
                    } else {
                        toValue = modelValue.to;
                    }
                }
                value = viewValue || modelValue;
                value = rule.needFilterModelValue ? $filter('encode')(value) : value;
                pattern = rule.pattern;
                if (!pattern) {
                    pattern = PATTERN_TYPES[angular.lowercase(rule.ruleName)];
                } else {
                    if (PATTERN_TYPES[pattern]) {
                        rule.errorMsg = "validation.patterncheck." + angular.lowercase(pattern);
                        pattern = PATTERN_TYPES[pattern];
                    } else {
                        for (var propKey in PATTERN_TYPES) {
                            var patternType = PATTERN_TYPES[propKey];
                            if (pattern == patternType) {
                                rule.errorMsg = "validation.patterncheck." + angular.lowercase(propKey);
                                break;
                            }
                        }
                    }
                    pattern = new RegExp(pattern);
                }
                if (isSpecialModelType(modelValue) && (!isEmptyWithOutTrim(fromValue) && !pattern.test(fromValue)) || (!isEmptyWithOutTrim(toValue) && !pattern.test(toValue))) {
                    buildErrorMsg(rule, deferred, $translate);
                } else if (!isSpecialModelType(modelValue) && !isEmpty(pattern) && (!isEmptyWithOutTrim(value) && !pattern.test(value))) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    holidayCheckService.$inject = ['$q', '$translate'];
    function holidayCheckService($q, $translate) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value = modelValue, dateDisabledFun, fromValue, toValue;
                if (rule._dateOptions) dateDisabledFun = rule._dateOptions.dateDisabled;
                if (modelValue && isSpecialModelType(modelValue)) {
                    fromValue = modelValue.from;
                    toValue = modelValue.to;
                } else {
                    value = modelValue;
                }
                function isInValid(value) {
                    return !isEmpty(value) && angular.isDate(value) && dateDisabledFun && dateDisabledFun({ date: value, mode: rule._dateOptions.datepickerMode });
                }
                if (!isSpecialModelType(modelValue) && isInValid(value)) {
                    buildErrorMsg(rule, deferred, $translate);
                } else if (isSpecialModelType(modelValue) && (isInValid(fromValue) || isInValid(toValue))) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }
    emailCheckService.$inject = ['$injector'];
    function emailCheckService($injector) {
        return $injector.get('patterncheck');
    }

    urlCheckService.$inject = ['$q', '$translate', '$injector', '$filter'];
    function urlCheckService($injector) {
        return $injector.get('patterncheck');
    }

    dateFormatCheckService.$inject = ['$q', '$translate', '$injector', 'formatService'];
    function dateFormatCheckService($q, $translate, $injector, formatService) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var defer = $q.defer(), patterncheck = $injector.get('patterncheck');
                if (viewValue == undefined) {
                    defer.resolve();
                    return defer.promise;
                }
                patterncheck.validate(modelValue, viewValue, rule).then(function (e) {
                    var dateStr = formatService.formatDate(formatService.parseDate(viewValue, rule.dateInputFormat), rule.dateInputFormat)
                    if (dateStr == viewValue) {
                        defer.resolve();
                    } else {
                        buildErrorMsg(rule, defer, $translate);
                    }
                }, function (e) {
                    buildErrorMsg(rule, defer, $translate);
                });
                return defer.promise;
            }
        };
    }


    uniqueCheckService.$inject = ['$q', '$translate', '$filter', '$parse', 'uniqueCheckService'];
    function uniqueCheckService($q, $translate, $filter, $parse, uniqueCheckService) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var checkedFields, checkedFieldArray, params = {}, hasNullValue = false, preModel = '', deferred = $q.defer();
                var value = rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue;
                if (!isEmpty(value)) {
                    checkedFields = rule.checkedFields;
                    checkedFieldArray = angular.isArray(checkedFields) ? checkedFields : checkedFields.split(",");
                    params['service'] = rule.service;
                    params['primaryKey'] = rule.ngModel ? $parse('primaryKey')(rule.ngModel) : null;
                    params['dataStatus'] = rule.ngModel ? $parse('dataStatus')(rule.ngModel) : null;
                    angular.forEach(checkedFieldArray, function (data, index, array) {
                        if (rule.operatedField == data) {
                            params[data] = value;
                        } else {
                            params[data] = rule.ngModel ? $parse(data)(rule.ngModel) : null;
                        }
                        if (isEmpty(params[data]))
                            hasNullValue = true;
                    });
                    if (!hasNullValue) {
                        uniqueCheckService.uniqueCheck(params).then(function (response) {
                            if (!response) {
                                rule.operatedFieldValue = value;
                                buildErrorMsg(rule, deferred, $translate);
                            } else {
                                deferred.resolve();
                            }
                        }, function (error) {
                            deferred.reject(error);
                        });
                    } else {
                        deferred.resolve();
                    }
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }

    equalsToService.$inject = ['$q', '$translate', '$filter', '$parse'];
    function equalsToService($q, $translate, $filter, $parse) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), value, targetValue;
                value = rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue;
                targetValue = rule.ngModel ? $parse(rule.target)(rule.ngModel) : null;
                if (!isEmpty(value) && !isEmpty(targetValue) && value != targetValue) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }
    dateCompareService.$inject = ['$q', '$translate', '$parse'];
    function dateCompareService($q, $translate, $parse) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), operateValue, targetValue, compareResult, isValid;
                operateValue = rule.ngModel ? $parse(rule.operatedField)(rule.ngModel) : null;
                targetValue = rule.ngModel ? $parse(rule.target)(rule.ngModel) : null;
                if (isEmpty(operateValue)) operateValue = rule.appScope ? $parse(rule.operatedField)(rule.appScope) : null;
                if (isEmpty(targetValue)) targetValue = rule.appScope ? $parse(rule.target)(rule.appScope) : null;
                if (!operateValue || !targetValue) {
                    deferred.resolve();
                } else {
                    if (rule.ignoreTime) {
                        operateValue = clearTime(angular.copy(operateValue));
                        targetValue = clearTime(angular.copy(targetValue));
                    }
                    compareResult = compareDateTime(operateValue, targetValue);
                    isValid = eval(compareResult + rule.symbol + '0');
                    if (!isValid) {
                        buildErrorMsg(rule, deferred, $translate);
                    } else {
                        deferred.resolve();
                    }

                }
                return deferred.promise;
            }
        };
    }
    dateBetweenService.$inject = ['$q', '$translate', '$parse'];
    function dateBetweenService($q, $translate, $parse) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), operateValue, fromValue, toValue, greaterFrom = true, lessTo = true, compareResult, allowEqual = rule.allowEqual;
                operateValue = rule.ngModel ? $parse(rule.operatedField)(rule.ngModel) : null;
                if (!angular.isArray(rule.target) || (angular.isArray(rule.target) && rule.target.length < 2)) {
                    deferred.resolve();
                    return;
                }
                fromValue = rule.ngModel ? $parse(rule.target[0])(rule.ngModel) : null;
                toValue = rule.ngModel ? $parse(rule.target[1])(rule.ngModel) : null;
                if (!operateValue || (!fromValue && !toValue)) {
                    deferred.resolve();
                } else {
                    if (rule.ignoreTime) {
                        operateValue = clearTime(angular.copy(operateValue));
                        fromValue = fromValue ? clearTime(angular.copy(fromValue)) : null;
                        toValue = toValue ? clearTime(angular.copy(toValue)) : null;
                    }
                    if (fromValue) {
                        compareResult = compareDateTime(operateValue, fromValue);
                        greaterFrom = eval(compareResult + (allowEqual ? ' >= 0' : ' > 0'));
                    }
                    if (toValue) {
                        compareResult = compareDateTime(operateValue, toValue);
                        lessTo = eval(compareResult + (allowEqual ? ' <= 0' : ' < 0'));
                    }
                    if (!greaterFrom || !lessTo) {
                        buildErrorMsg(rule, deferred, $translate);
                    } else {
                        deferred.resolve();
                    }
                }
                return deferred.promise;
            }
        };
    }
    numberCompareService.$inject = ['$q', '$translate', '$parse'];
    function numberCompareService($q, $translate, $parse) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), operateValue, targetValue, isValid = true;
                operateValue = rule.ngModel ? $parse(rule.operatedField)(rule.ngModel) : null;
                targetValue = rule.ngModel ? $parse(rule.target)(rule.ngModel) : null;
                if (isEmpty(operateValue) || isEmpty(targetValue)) {
                    deferred.resolve();
                } else {
                    operateValue = Number(operateValue);
                    targetValue = Number(targetValue);
                    if (operateValue > targetValue) {
                        isValid = 1;
                    } else if (operateValue < targetValue) {
                        isValid = -1;
                    } else {
                        isValid = 0;
                    }
                    isValid = eval(isValid + rule.symbol + '0');
                    if (!isValid) {
                        buildErrorMsg(rule, deferred, $translate);
                    } else {
                        deferred.resolve();
                    }
                }
                return deferred.promise;
            }
        };
    }

    rowCompareService.$inject = ['$q', '$translate'];
    function rowCompareService($q, $translate) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), currentValue, nextIndex, nextValue, isValid = true, rowDatas = rule.rowDatas, operatedField = rule.operatedField,
                    symbol = rule.symbol, extraControlField = rule.extraControlField, extraControlValue = rule.extraControlValue, extraControlSymbol = rule.extraControlSymbol;
                for (var index = 0; index < rowDatas.length; index++) {
                    currentValue = rowDatas[index][operatedField];
                    nextIndex = index + 1;
                    if (nextIndex < rowDatas.length) {
                        nextValue = rowDatas[nextIndex][operatedField];

                        if (extraControlField && rowDatas[nextIndex][extraControlField] == extraControlValue) {
                            symbol = extraControlSymbol;
                        } else {
                            symbol = rule.symbol;
                        }
                    } else {
                        nextValue = null;
                    }
                    if (!isEmpty(currentValue) && !isEmpty(nextValue)) {
                        currentValue = Number(currentValue);
                        nextValue = Number(nextValue);
                        if (nextValue > currentValue) {
                            isValid = 1;
                        } else if (nextValue < currentValue) {
                            isValid = -1;
                        } else {
                            isValid = 0;
                        }
                        isValid = eval(isValid + symbol + '0');
                        if (!isValid) {
                            if (rule.symbol.indexOf(">") != -1) {
                                rule.rowNum = index + 2;
                            } else {
                                rule.rowNum = index + 1;
                            }
                            break;
                        } else {
                            isValid = true;
                        }
                    }
                }
                if (!isValid) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }
    rowUniqueCheckService.$inject = ['$q', '$translate'];
    function rowUniqueCheckService($q, $translate) {
        return {
            validate: function (modelValue, viewValue, rule) {
                var deferred = $q.defer(), emptyFieldValueFlag = false, isValid = true, rowDatas = rule.rowDatas, checkedFieldStr = "", jsonMap = {}, checkedFields = rule.checkedFields;
                var index, j, checkedValue;
                if (!checkedFields) return;
                for (index = 0; index < rowDatas.length; index++) {
                    checkedFieldStr = "";
                    for (j = 0; j < checkedFields.length; j++) {
                        checkedValue = rowDatas[index][checkedFields[j]];
                        if (isEmpty(checkedValue)) {
                            emptyFieldValueFlag = true;
                            break;
                        } else {
                            checkedFieldStr += checkedValue + "_";
                            emptyFieldValueFlag = false;
                        }
                    }
                    if (emptyFieldValueFlag) {
                        isValid = true;
                        break;
                    }
                    if (jsonMap && jsonMap[checkedFieldStr]) {
                        isValid = false;
                        break;
                    } else {
                        jsonMap[checkedFieldStr] = checkedFieldStr;
                        isValid = true;
                    }
                }
                if (!isValid) {
                    buildErrorMsg(rule, deferred, $translate);
                } else {
                    deferred.resolve();
                }
                return deferred.promise;
            }
        };
    }
    validateUtil.$inject = ['$filter'];
    function validateUtil($filter) {
        return {
            registerCompareCheck: registerCompareCheck,
            getCompareRules: getCompareRules,
            registerRowCompareCheck: registerRowCompareCheck,
            getRowCompareRules: getRowCompareRules,
            registerUniqueCheckCheck: registerUniqueCheckCheck,
            getUniqueCheckRules: getUniqueCheckRules,
            registerDependCheck: registerDependCheck,
            dependCheckByConditionChanged: dependCheckByConditionChanged
        };
        function registerCompareCheck(rule) {
            var i, registerKeys, resultSet;
            if (!rule || !rule.fieldCtrl || !rule.operatedField || !rule.target) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            registerKeys = angular.isArray(rule.target) ? rule.target : [rule.target];
            registerKeys.push(rule.operatedField);
            for (i = 0; i < registerKeys.length; i++) {
                if (!resultSet.__compareCheck) {
                    resultSet.__compareCheck = {};
                    resultSet.__compareCheck[registerKeys[i]] = {
                        rules: [rule],
                        validate: validateFn
                    };
                    break;
                } else if (resultSet.__compareCheck[registerKeys[i]]) {
                    resultSet.__compareCheck[registerKeys[i]].rules.push(rule);
                    break;
                }
            }
            if (i >= registerKeys.length) {
                resultSet.__compareCheck[rule.operatedField] = {
                    rules: [rule],
                    validate: validateFn
                };
            }
        }
        function getCompareRules(rule) {
            var i, registerKeys, checker, resultSet;
            if (!rule || !rule.fieldCtrl || !rule.operatedField || !rule.target) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            registerKeys = angular.isArray(rule.target) ? angular.copy(rule.target) : [angular.copy(rule.target)];
            registerKeys.push(rule.operatedField);
            for (i = 0; i < registerKeys.length; i++) {
                if (resultSet.__compareCheck[registerKeys[i]]) {
                    checker = resultSet.__compareCheck[registerKeys[i]];
                    break;
                }
            }
            if (checker) {
                return checker.rules;
            }
            return null;
        }
        function registerRowCompareCheck(rule) {
            var i, registerKeys = [], resultSet;
            if (!rule || !rule.fieldCtrl || !rule.operatedField) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            registerKeys.push(rule.operatedField);
            for (i = 0; i < registerKeys.length; i++) {
                if (!resultSet.__compareCheck) {
                    resultSet.__compareCheck = {};
                    resultSet.__compareCheck[registerKeys[i]] = {
                        rules: [rule],
                        validate: validateFn
                    };
                    break;
                } else if (resultSet.__compareCheck[registerKeys[i]]) {
                    resultSet.__compareCheck[registerKeys[i]].rules.push(rule);
                    break;
                }
            }
            if (i >= registerKeys.length) {
                resultSet.__compareCheck[rule.operatedField] = {
                    rules: [rule],
                    validate: validateFn
                };
            }
        }
        function getRowCompareRules(rule) {
            var i, registerKeys = [], checker, resultSet;
            if (!rule || !rule.fieldCtrl || !rule.operatedField) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            registerKeys.push(rule.operatedField);
            for (i = 0; i < registerKeys.length; i++) {
                if (resultSet.__compareCheck[registerKeys[i]]) {
                    checker = resultSet.__compareCheck[registerKeys[i]];
                    break;
                }
            }
            if (checker) {
                return checker.rules;
            }
            return null;
        }

        function registerUniqueCheckCheck(rule) {
            var i, registerKeys, resultSet;
            if (!rule || !rule.fieldCtrl || !rule.operatedField || !rule.checkedFields) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            registerKeys = angular.isArray(rule.checkedFields) ? rule.checkedFields : [rule.checkedFields];
            for (i = 0; i < registerKeys.length; i++) {
                if (!resultSet.__uniqueCheck) {
                    resultSet.__uniqueCheck = {};
                    resultSet.__uniqueCheck[registerKeys[i]] = {
                        rules: [rule],
                        validate: validateFn
                    };
                    break;
                } else if (resultSet.__uniqueCheck[registerKeys[i]]) {
                    resultSet.__uniqueCheck[registerKeys[i]].rules.push(rule);
                    break;
                }
            }
            if (i >= registerKeys.length) {
                resultSet.__uniqueCheck[rule.operatedField] = {
                    rules: [rule],
                    validate: validateFn
                };
            }
        }
        function getUniqueCheckRules(rule) {
            var i, registerKeys, checker, resultSet;
            if (!rule || !rule.fieldCtrl || !rule.operatedField || !rule.checkedFields) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            registerKeys = angular.isArray(rule.checkedFields) ? rule.checkedFields : [rule.checkedFields];
            for (i = 0; i < registerKeys.length; i++) {
                if (resultSet.__uniqueCheck[registerKeys[i]]) {
                    checker = resultSet.__uniqueCheck[registerKeys[i]];
                    break;
                }
            }
            if (checker) {
                return checker.rules;
            }
            return null;
        }
        function registerDependCheck(rule) {
            var resultSet, conditionName, conditionValue;
            if (!rule || !rule.appScope || !rule.conditionField || (!rule.conditionValue && (rule.conditionValue != 0 || rule.conditionValue !== false)) || !rule.fieldCtrl) {
                return;
            }
            resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope;
            conditionName = rule.conditionField;
            conditionValue = rule.conditionValue;
            if (!angular.isArray(conditionValue)) {
                conditionValue = [conditionValue];
            }
            if (resultSet.__dependCheck && resultSet.__dependCheck[conditionName]) {
                angular.forEach(conditionValue, function (value) {
                    if (resultSet.__dependCheck[conditionName][value]) {
                        resultSet.__dependCheck[conditionName][value].rules.push(rule);
                    } else {
                        resultSet.__dependCheck[conditionName][value] = {
                            rules: [rule],
                            validate: validateFn
                        };
                    }
                });
            } else {
                resultSet.__dependCheck = resultSet.__dependCheck || {};
                resultSet.__dependCheck[conditionName] = resultSet.__dependCheck[conditionName] || {};
                angular.forEach(conditionValue, function (value) {
                    resultSet.__dependCheck[conditionName][value] = {
                        rules: [rule],
                        validate: validateFn
                    };
                });
            }
        }
        function dependCheckByConditionChanged(rule, modelValue) {
            var checker, resultSet = rule.uiGridRow ? rule.uiGridRow : rule.appScope, conditionName = rule.operatedField, conditionValue = (rule.needFilterModelValue ? $filter('encode')(modelValue) : modelValue);
            if (resultSet.__dependCheck && resultSet.__dependCheck[conditionName]) {
                angular.forEach(resultSet.__dependCheck[conditionName], function (checker) {
                    checker.validate(checker.rules);
                });
            }
        }
    }

    uniqueCheckServiceFactory.$inject = ['$q', 'ajaxServiceFactory'];
    function uniqueCheckServiceFactory($q, ajaxServiceFactory) {
        var ajaxService = ajaxServiceFactory.buildAjaxService({
            uniqueCheck: {
                url: 'do/validator/uniqueCheck',
                method: 'post'
            }
        });
        return {
            //params like {countryCode : 'CN', cityCode : 'beijing'}
            uniqueCheck: function (params) {
                var defer = $q.defer();
                ajaxService.uniqueCheck(params).then(function (response) {
                    if (response) {
                        defer.resolve(response.data);
                    }
                });
                return defer.promise;
            }
        };
    }

    lookupCheckServiceFactory.$inject = ['$q', 'ajaxServiceFactory'];
    function lookupCheckServiceFactory($q, ajaxServiceFactory) {
        var ajaxService = ajaxServiceFactory.buildAjaxService({
            lookupCheck: {
                url: 'do/validator/lookupCheck',
                method: 'post'
            }
        });
        return {
            lookupCheck: function (params) {
                var defer = $q.defer();
                ajaxService.lookupCheck(params).then(function (response) {
                    if (response) {
                        defer.resolve(response.data);
                    }
                });
                return defer.promise;
            }
        }
    }

    function isEmpty(value) {
        return (value === null || angular.isUndefined(value) || (angular.isString(value) ? value.trim() == '' : false || (angular.isArray(value) && value.length == 0)));
    }
    function isEmptyWithOutTrim(value) {
        return (value === null || angular.isUndefined(value) || (angular.isString(value) ? value == '' : false || (angular.isArray(value) && value.length == 0)));
    }
    function isSpecialModelType(value) {
        if (angular.isObject(value) && !angular.isDate(value) && !angular.isArray(value)) {
            return true;
        } else {
            return false;
        }
    }
    function validateFn(rules) {
        angular.forEach(rules, function (rule) {
            rule.fieldCtrl.$validate();
        });
    }
    function buildErrorMsg(rule, deferred, $translate) {
        var paramObj = {}, errorArgs = rule.errorArgs, errorMsg = rule.errorMsg;
        if (!errorArgs || !errorMsg) {
            deferred.reject({
                error: 'validator errorArgs or errorMsg is not exist in validator json file'
            });
        }
        $translate(errorArgs).then(function (objs) {
            if ("uniquecheck" == angular.lowercase(rule.ruleName) || "lookupcheck" == angular.lowercase(rule.ruleName)) {
                angular.forEach(errorArgs, function (key, index) {
                    if (errorArgs.length == 2) {
                        paramObj['p0'] = objs[errorArgs[0]];
                        paramObj['p1'] = rule.operatedFieldValue;
                        paramObj['p2'] = objs[errorArgs[1]];
                    }
                    if (errorArgs.length == 1) {
                        paramObj['p0'] = objs[errorArgs[0]];
                        paramObj['p1'] = rule.operatedFieldValue;
                    }
                });
            } else if ("dependent" == angular.lowercase(rule.ruleName) && rule.conditionPattern) {
                paramObj['p0'] = objs[errorArgs[0]];
                paramObj['p1'] = rule.conditionPattern.split(";")[rule.conditionFieldValueIndex];
                if (rule.validateRequiredFlag) {
                    errorMsg = "validation.required";
                } else {
                    errorMsg = errorMsg.split(";")[rule.conditionFieldValueIndex];
                }
            } else if ("rowcompare" == angular.lowercase(rule.ruleName)) {
                paramObj['p0'] = objs[errorArgs[0]];
                paramObj['p1'] = rule.rowNum;
            } else {
                angular.forEach(errorArgs, function (key, index) {
                    paramObj['p' + index] = objs[errorArgs[index]];
                });
            }

            $translate(errorMsg, paramObj).then(function (errorMsg) {
                deferred.reject({
                    error: errorMsg
                });
            }, function (msg) {
                deferred.reject({
                    error: msg
                });
            });
        }, function (msg) {
            deferred.reject({
                error: msg
            });
        });
    }
    function clearTime(date) {
        if (angular.isDate(date)) {
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(0);
            date.setMilliseconds(0);
        }
        return date;
    }
    function compareDateTime(date1, date2) {
        if (!angular.isDate(date1) || !angular.isDate(date2)) {
            return 0;
        }
        var time1 = date1.getTime();
        var time2 = date2.getTime();
        if (time2 > time1) {
            return -1;
        } else if (time2 == time1) {
            return 0;
        } else {
            return 1;
        }
    }
})(angular);